﻿using System;
using System.Collections.Generic;
using System.Text;

using System.Net;
using System.Net.Sockets;

namespace LiBLaB.Networking.Asynchronous
{
    public delegate void connectionEstablished_delegate(bool status);
    public delegate void dataReceived_delegate(byte[] buffer);

    public class Client
    {
        private Socket client;

        public string ServerIP
        {
            get
            {
                if (client.Connected)
                    return client.RemoteEndPoint.ToString();
                else
                    return "Can't reach Server...";
            }
        }

        public void Connect(string ServerIP, int Port, connectionEstablished_delegate connCallBack)
        {
            client = new Socket(AddressFamily.InterNetwork,
                       SocketType.Stream, ProtocolType.Tcp);
            IPEndPoint iep = new IPEndPoint(IPAddress.Parse(ServerIP), Port);
            client.BeginConnect(iep, new AsyncCallback(ConnecCallBack), new object[2] { client, connCallBack });
        }

        void ConnecCallBack(IAsyncResult iar)
        {
            object[] state = (object[])iar.AsyncState;
            client = (Socket)state[0];
            connectionEstablished_delegate connDelgate = (connectionEstablished_delegate)state[1];
            try
            {
                client.EndConnect(iar);
                connDelgate(true);
            }
            catch (SocketException)
            {
                connDelgate(false);
            }
        }

        public void Receive(dataReceived_delegate connCallBack)
        {
            if (client != null && client.Connected)
            {
                byte[] buffer = new byte[sizeof(int)];
                client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(BeginReceiveCallBack), new object[3] { client, buffer, connCallBack });
            }
        }

        void BeginReceiveCallBack(IAsyncResult iar)
        {
            object[] state = (object[])iar.AsyncState;
            Socket server = (Socket)state[0];
            byte[] buffer = (byte[])state[1];
            dataReceived_delegate connCallBack = (dataReceived_delegate)state[2];

            int recv = server.EndReceive(iar);
            int buffer_size = BitConverter.ToInt32(buffer, 0);
            buffer = new byte[buffer_size];
            client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), new object[3] { server, buffer, connCallBack });
        }

        void ReceiveCallBack(IAsyncResult iar)
        {
            object[] state = (object[])iar.AsyncState;
            Socket server = (Socket)state[0];
            byte[] buffer = (byte[])state[1];
            dataReceived_delegate connCallBack = (dataReceived_delegate)state[2];
            int recv = server.EndReceive(iar);
            connCallBack(buffer);
        }

        public void Send(byte[] buffer)
        {
            if (client != null && client.Connected)
            {
                byte[] buffer_size = new byte[sizeof(int)];
                buffer_size = BitConverter.GetBytes(buffer.Length);
                client.BeginSend(buffer_size, 0, buffer_size.Length, SocketFlags.None, new AsyncCallback(BeginSendCallBack), new object[2] { client, buffer });
            }
        }

        void BeginSendCallBack(IAsyncResult iar)
        {
            object[] state = (object[])iar.AsyncState;
            Socket server = (Socket)state[0];
            byte[] buffer = (byte[])state[1];
            int sent = server.EndSend(iar);
            client.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(SendCallBack), state);
        }

        void SendCallBack(IAsyncResult iar)
        {
            object[] state = (object[])iar.AsyncState;
            Socket server = (Socket)state[0];
            int sent = server.EndSend(iar);
        }

        public void Dissconnect()
        {
            if(client!= null)
                client.Close();
        }
    }
}
